package org.benetech.util;

import java.util.Iterator;

import org.benetech.beans.Filter;
import org.benetech.collections.Tree;
import org.jdom.Document;
import org.jdom.Element;

/**
 * Utilities to replicate JDOM objects into benetech Tree structures.
 * @author Reuben Firmin
 */
public final class XMLTreeUtils {

	/**
	 * Non constructor.
	 */
	private XMLTreeUtils() {
		// empty
	}

	/**
     * Return a tree built from the structure of the document. The root element is always at the root of the tree.
     * However, all other child elements are passed through the attached filter, and only added to the tree if they
     * pass. Elements not added still have their children processed, which are attached to the parent if they in turn
     * pass.
     *
     * @param document The document to parse
     * @param filter The filter to execute against elements. Null for no filtering
     * @return Never null
     */
    public static Tree<Element, Object, Object> getDocumentAsTree(final Document document,
    		final Filter<Element, ? > filter)
    {
    	final Element root = document.getRootElement();
    	final Tree<Element, Object, Object> documentTree = new Tree<Element, Object, Object>(document.getRootElement());
    	final Iterator<Element> children = (Iterator<Element>) root.getChildren().iterator();
    	while (children.hasNext()) {
    		final Element child = children.next();
    		XMLTreeUtils.addElementAndChildrenToTree(child, filter, documentTree);
    	}
    	return documentTree;
    }

	/**
     * Return a tree built from the structure of the element. All child elements are passed through the attached filter,
     * and only added to the tree if they pass. Elements not added still have their children processed, which are
     * attached to the parent if they in turn pass.
     *
     * @param element The element to parse
     * @param filter The filter to execute against elements. Null for no filtering
     * @return Never null
     */
    public static Tree<Element, Object, Object> getElementAsTree(final Element element,
    		final Filter<Element, ? > filter)
    {
    	final Tree<Element, Object, Object> documentTree = new Tree<Element, Object, Object>(element);
    	final Iterator<Element> children = (Iterator<Element>) element.getChildren().iterator();
    	while (children.hasNext()) {
    		final Element child = children.next();
    		XMLTreeUtils.addElementAndChildrenToTree(child, filter, documentTree);
    	}
    	return documentTree;
    }

	/**
     * Adds the element and its children to the tree, if they are accepted by the filter. Elements which don't get past
     * the filter are skipped, and their children (if applicable) possibly attached to the parent.
     *
     * @param element The element to add
     * @param filter The filter to apply; if null, pass everything
     * @param parent The parent tree to add to
     */
    public static void addElementAndChildrenToTree(final Element element, final Filter<Element, ? > filter,
            final Tree<Element, Object, Object> parent)
    {
    	Tree<Element, Object, Object> childParent;
    	if (filter == null || filter.accept(element)) {
    		childParent = new Tree<Element, Object, Object>(element);
    		parent.addChild(childParent);
    	} else {
    		childParent = parent;
    	}

    	final Iterator<Element> children = (Iterator<Element>) element.getChildren().iterator();
    	while (children.hasNext()) {
    		final Element child = children.next();
    		addElementAndChildrenToTree(child, filter, childParent);
    	}
    }

}
